/* This file is part of Sawa, the Scriptic-Java compiler
 * Copyright (C) 2009 Andre van Delft
 *
 * Sawa is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package scriptic.tools;


import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import scriptic.tools.lowlevel.Instruction;
import scriptic.tools.lowlevel.ByteCodingException;

public class ScripticPreprocessor
   implements  scriptic.tokens.ScripticTokens, ModifierFlags {

   Scanner scanner;
   PreprocessorOutputStream outputStream;
   CompilerEnvironment env;

   public ScripticPreprocessor (Scanner scanner, 
                                PreprocessorOutputStream outputStream,
                                CompilerEnvironment env) {
      this.outputStream = outputStream;
      this.scanner      = scanner;
      this.env          = env;
   }

   /* Main entry point */
   public boolean resolve (CompilationUnit c) {

      outputStream.println ();
      outputStream.println ("/*********************************************************************/");
      outputStream.println ("/*                                                                   */");
      outputStream.println ("/*                                                                   */");
      outputStream.println ("/*             S C R I P T I C   P R E P R O C E S S O R             */");
      outputStream.println ("/*                                                                   */");
      outputStream.println ("/*                                                                   */");
      outputStream.println ("/*********************************************************************/");
      outputStream.println ();
      outputStream.println ("/*");
      outputStream.println ("      This file was generated by the Scriptic preprocessor.");
      outputStream.println ();
      outputStream.println ("      Source file:           " + c.getName ());
      outputStream.println ("      Code generated on:     " + new Date ().toString ());
      outputStream.println ("      Preprocessor version:  1.0.0");
      outputStream.println ();
      outputStream.println ("*/");
      outputStream.println ();
      outputStream.println ();

      /* Generate in-class code -- templates, methods, codeprocs */
      ScriptGenerator scriptGenerator =
                  new ScriptGenerator (scanner, outputStream, env);

      //System.out.println("ScriptGenerator.scanner : "+scanner);
      ReturnValueHolder retValue = new ReturnValueHolder ();
      scriptGenerator.processCompilationUnit (c, retValue, null, null);

      return true;
   }
}



   /*******************************************************************/
   /**                                                               **/
   /**                        ScriptGenerator                        **/
   /**                                                               **/
   /*******************************************************************/

class ScriptGenerator extends JavaCodeGenerator {

   /*-------------------------- Constructors -------------------------*/

   public ScriptGenerator (Scanner                  scanner,
                           PreprocessorOutputStream outputStream,
                           CompilerEnvironment      env) {
      super (scanner, outputStream, env);
   }

   /*-----------------------------------------------------------------*/
   /*                                                                 */
   /*                        Script Declarations                      */
   /*                                                                 */
   /*-----------------------------------------------------------------*/

   protected void processScriptDeclaration (ScriptDeclaration scriptDeclaration, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {

      outLine   ();
      if (   scriptDeclaration.typeDeclaration.isInterface ()
          || scriptDeclaration.isNative ()   // illegal, but non-fatal
          || scriptDeclaration.isAbstract ()) {

         outScriptMethod        ((BasicScriptDeclaration) scriptDeclaration);
      } else {
         outScriptTemplate      (scriptDeclaration);
         outScriptMethod        ((BasicScriptDeclaration) scriptDeclaration);
         outLine   ();
         outScriptCodeProc      ((BasicScriptDeclaration) scriptDeclaration);
      }
      outLine   ();
   }

   /* ------------------------------------------------------------------ */

   protected void outScriptTemplate (ScriptDeclaration scriptDeclaration) {
      String templateName;
      outScriptHeaderComment ((BasicScriptDeclaration) scriptDeclaration, " -- template");

      templateName = scriptDeclaration.getTemplateName();
      outTemplateDeclaration (scriptDeclaration, scriptDeclaration.templateGetMethod, templateName, false);
   }


   /* ------------------------------------------------------------------ */

   protected void outScriptCodeProc (BasicScriptDeclaration scriptDeclaration) {

      outScriptHeaderComment ((BasicScriptDeclaration) scriptDeclaration, " -- codeproc");

      CodeProcGenerator codeProcGenerator = new CodeProcGenerator (scanner, outputStream, env);
      codeProcGenerator.processScriptExpression (scriptDeclaration, 
                                                 scriptDeclaration.scriptExpression);
   }


   /*-----------------------------------------------------------------*/
   /*                                                                 */
   /*                    Communication Declarations                   */
   /*                                                                 */
   /*-----------------------------------------------------------------*/

   protected void processCommunicationDeclaration (CommunicationDeclaration commDeclaration, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {

      outLine   ();
      if (   commDeclaration.typeDeclaration.isInterface ()
          || commDeclaration.isNative ()   // illegal, but non-fatal
          || commDeclaration.isAbstract ()) {

      } else {
         outCommunicationTemplates (commDeclaration);
         outCommPartnerMethodsAndClasses(commDeclaration);
         outScriptCodeProc      ((BasicScriptDeclaration) commDeclaration);
      }
      outLine   ();
   }

   /* ------------------------------------------------------------------ */

   protected void outCommunicationTemplates (CommunicationDeclaration commDeclaration) {
      String partnerTemplateName, commTemplateName;
      ScriptStringGenerator scriptStringGenerator = new ScriptStringGenerator (scanner);
      scriptStringGenerator.processScriptExpression (commDeclaration, 
                                                     commDeclaration.scriptExpression);

      for (BasicScriptDeclaration bsd: commDeclaration.partners) {
     	 CommunicationPartnerDeclaration partner = (CommunicationPartnerDeclaration) bsd;
         if (partner.firstOccurrence != null)
            continue;

         partnerTemplateName = partner.getPartnerTemplateName ();
         //partnerScriptString = partner.getPartnerScriptStringName ();
         outScriptHeaderComment ((BasicScriptDeclaration) partner, " -- template");
         outTemplateDeclaration (partner, partner.templateGetMethod, partnerTemplateName, true);
      }

      commTemplateName = commDeclaration.getTemplateName ();
      scriptStringGenerator.getScriptString ();
      outScriptHeaderComment ((BasicScriptDeclaration) commDeclaration, " -- template");
      outTemplateDeclaration (commDeclaration, commDeclaration.templateGetMethod,commTemplateName, false);

   }


   /* ------------------------------------------------------------------ */

   protected void outCommPartnerMethodsAndClasses (CommunicationDeclaration commDeclaration) {

      for (BasicScriptDeclaration bsd: commDeclaration.partners) {
       	 CommunicationPartnerDeclaration partner = (CommunicationPartnerDeclaration) bsd;
         if (partner.firstOccurrence != null)
            continue;

         outScriptMethod ((BasicScriptDeclaration) partner);
      }
   }


   /*-----------------------------------------------------------------*/
   /*                                                                 */
   /*                       Channel Declarations                      */
   /*                                                                 */
   /*-----------------------------------------------------------------*/


   protected void processChannelDeclaration (ChannelDeclaration channelDeclaration, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      outLine   ();
      outScriptHeaderComment ((BasicScriptDeclaration) channelDeclaration);

      if (   channelDeclaration.typeDeclaration.isInterface ()
          || channelDeclaration.isNative ()   // illegal, but non-fatal
          || channelDeclaration.isAbstract ()) {

         outScriptMethod    ((BasicScriptDeclaration) channelDeclaration);
      } else {
         outChannelTemplates (channelDeclaration);
         outScriptMethod     ((BasicScriptDeclaration) channelDeclaration);
         outScriptCodeProc   ((BasicScriptDeclaration) channelDeclaration);
      }
      outLine   ();
   }


   /* ------------------------------------------------------------------ */

   protected void outChannelTemplates (ChannelDeclaration channelDeclaration) {
      String partnerTemplateName, channelTemplateName;
 
      ScriptStringGenerator scriptStringGenerator = new ScriptStringGenerator (scanner);
      scriptStringGenerator.processScriptExpression (channelDeclaration, 
                                                     channelDeclaration.scriptExpression);

      partnerTemplateName = channelDeclaration.getPartnerTemplateName ();
      channelTemplateName = channelDeclaration.getTemplateName ();

      outScriptHeaderComment ((BasicScriptDeclaration) channelDeclaration, " -- partner template");
      outTemplateDeclaration (channelDeclaration, 
    		  				  channelDeclaration.partnerTemplateGetMethod,
    		  				  partnerTemplateName,
    		  				  true);

      outScriptHeaderComment ((BasicScriptDeclaration) channelDeclaration, " -- template");
      outTemplateDeclaration (channelDeclaration, 
    		                  channelDeclaration.templateGetMethod, 
    		                  channelTemplateName,
    		                  false);
   }


   /*-----------------------------------------------------------------*/
   /*                                                                 */
   /*                           Common routines                       */
   /*                                                                 */
   /*-----------------------------------------------------------------*/

   protected void outTemplateDeclaration (BasicScriptDeclaration scriptDeclaration, 
                                          ScriptTemplateGetMethod m, 
                                          String templateName, 
                                          boolean doChannelPartner) {

	// static scriptic.vm.NodeTemplate a_template;
	//
	// static scriptic.vm.NodeTemplate get_a_template () {
	//    if (a_template == null)
	//       a_template = scriptic.vm.NodeTemplate.makeTemplate
	//             ("packageName", "className", ..., templateArray);
	//    return a_template;
	// }

	    List<ScriptTemplateGetMethod> relatedTemplateGetMethods = new ArrayList<ScriptTemplateGetMethod>();
	    //ScriptTemplateGetMethod m = doChannelPartner
	    //	? ((ChannelDeclaration)scriptDeclaration).partnerTemplateGetMethod
    	//	: scriptDeclaration.templateGetMethod;
        String codeMethodName = (m instanceof ScriptPartnerTemplateGetMethod)
	      ? null
	      : m.scriptDeclaration().getCodeMethodName();   
	    if (scriptDeclaration instanceof CommunicationDeclaration) {
	    	for (CommunicationPartnerDeclaration partner: ((CommunicationDeclaration) scriptDeclaration).partners)
	    	{
	    		relatedTemplateGetMethods.add(partner.templateGetMethod);
	    	}
	    }
	    else if (scriptDeclaration instanceof CommunicationPartnerDeclaration) {
	    	for (CommunicationDeclaration communication: ((CommunicationPartnerDeclaration) scriptDeclaration).communications)
	    	{
	    		relatedTemplateGetMethods.add(communication.templateGetMethod);
	    	}
	    }
	    else if (scriptDeclaration instanceof ChannelDeclaration) {
	    	ChannelDeclaration channelDeclaration = (ChannelDeclaration)scriptDeclaration;
	  		relatedTemplateGetMethods.add(
	  				doChannelPartner? channelDeclaration.templateGetMethod
	  								: channelDeclaration.partnerTemplateGetMethod);
	    }
	   
      outToken  (PrivateToken);
      outSpace  ();
      outToken  (StaticToken);
      outSpace  ();
      outString ("scriptic.vm.NodeTemplate");
      outSpace  ();
      outString (templateName);
      outToken  (SemicolonToken);
      outLine   ();
      outLine   ();

      outToken  (PrivateToken);
      outSpace  ();
      outToken  (StaticToken);
      outSpace  ();
      outString ("scriptic.vm.NodeTemplate");
      outSpace  ();
      outString (templateName);
      outSpace  ();
      outToken  (ParenthesisOpenToken);
      outToken  (ParenthesisCloseToken);
      outSpace  ();
      outToken  (BraceOpenToken);
      indent    ();
      outLine   ();

      outToken  (IfToken);
      outSpace  ();
      outToken  (ParenthesisOpenToken);
      outString (templateName);
      outSpace  ();
      outToken  (EqualsToken);
      outSpace  ();
      outToken  (NullToken);
      outToken  (ParenthesisCloseToken);
      indent    ();
      outLine   ();

      outString (templateName);
      outSpace  ();
      outToken  (AssignToken);
      outSpace  ();
      outString ("scriptic.vm.NodeTemplate.makeNodeTemplate");
      indent    (4);
      outLine   ();
      outToken  (ParenthesisOpenToken);
      setIndent ();

      outQuotedString (scriptDeclaration.typeDeclaration.target.packageNameWithDots());
      outToken  (CommaToken);
      outQuotedString (scriptDeclaration.typeDeclaration.target.className);
      outToken  (CommaToken);
      outQuotedString (scriptDeclaration.name);
      outToken  (CommaToken);
      outQuotedString (scriptDeclaration.getParameterSignature(env));
      outToken  (CommaToken);
      if (codeMethodName==null)
      {
    	  outToken(NullToken);
      }
      else
      {
    	  outQuotedString(codeMethodName);
      }
      outToken  (CommaToken);
      outInteger(scriptDeclaration.modifiers.modifierFlags);
      outToken  (CommaToken);
      outToken  (ParenthesisOpenToken);
      outToken  (ShortToken);
      outToken  (ParenthesisCloseToken);
      outInteger(scriptDeclaration.getParameters().size());
      outToken  (CommaToken);
      outLine   ();

      ScriptTemplateArrayGenerator scriptTemplateArrayGenerator
                               = new ScriptTemplateArrayGenerator (env, scanner, typeDeclaration, typeDeclaration.target); 
      Object templateArray[][] =     scriptTemplateArrayGenerator.makeTemplateArrayFor(m);

      outToken  (NewToken);
      outSpace  ();
      outString ("Object");
      outToken  (BracketOpenToken); outToken (BracketCloseToken);
      outToken  (BracketOpenToken); outToken (BracketCloseToken);
      outToken  (BraceOpenToken);
      indent    (4);
      for (int i=0; i<templateArray.length; i++)
      {
        if (i>0) outToken (CommaToken);
        outLine   ();
        outToken  (NewToken);
        outSpace  ();
        outString ("Object");
        outToken  (BracketOpenToken); outToken (BracketCloseToken);
        outToken  (BraceOpenToken);
        for (int j=0; j<templateArray[i].length; j++)
        {
          Object array = templateArray[i][j];
          if (j>0) outToken (CommaToken);
          outToken  (NewToken);
          outSpace  ();
          if (array instanceof int[])
          {
            outString ("int");
            outToken  (BracketOpenToken); outToken (BracketCloseToken);
            outToken  (BraceOpenToken);
            for (int k=0; k<((int[])array).length; k++)
            {
              if (k>0) outToken (CommaToken);
              outInteger(((int[])array)[k]);
            }
            outToken  (BraceCloseToken);
          }
          else if (array instanceof long[])
          {
            outString ("long");
            outToken  (BracketOpenToken); outToken (BracketCloseToken);
            outToken  (BraceOpenToken);
            for (int k=0; k<((long[])array).length; k++)
            {
              if (k>0) outToken (CommaToken);
              outLong(((long[])array)[k]);
            }
            outToken  (BraceCloseToken);
          }
          else if (array instanceof String[])
          {
            outString ("String");
            outToken  (BracketOpenToken); outToken (BracketCloseToken);
            outToken  (BraceOpenToken);
            for (int k=0; k<((String[])array).length; k++)
            {
              if (k>0) outToken (CommaToken);
              outQuotedString(((String[])array)[k]);
            }
            outToken  (BraceCloseToken);
          }
          // else nothing !
        }
        outToken  (BraceCloseToken);
      }
      unindent();
      outToken  (BraceCloseToken);
      outLine();

//      startMultilineStringLiteral ();
//      outString (scriptString);
//      stopMultilineStringLiteral ();

      outToken  (ParenthesisCloseToken);
      outToken  (SemicolonToken);
      unindent  ();
      unindent  ();
      unindent  ();
      outLine   ();

      if (relatedTemplateGetMethods.size()>0)
      {
          outLine   ();
          outString (templateName);
          outToken  (SemicolonToken);
          outString ("setRelatedTemplates");
          outToken  (ParenthesisOpenToken);
          outToken  (NewToken);
          outSpace  ();
          outString ("scriptic.vm.NodeTemplate");
          outToken  (BracketOpenToken);
          outToken  (BracketCloseToken);
          outToken  (BraceOpenToken);
          for (int i=0; i<relatedTemplateGetMethods.size(); i++)
          {
        	 if (i>0) {
                 outToken  (CommaToken);
        	 }
          	 ScriptTemplateGetMethod relatedTemplateGetMethod = relatedTemplateGetMethods.get(i);
        	 outString (relatedTemplateGetMethod.name);
             outToken  (ParenthesisOpenToken);
             outToken  (ParenthesisCloseToken);
          }
          outToken  (BraceCloseToken);
          outToken  (ParenthesisCloseToken);
          outToken  (SemicolonToken);
          outLine   ();
      }
      
      outToken  (ReturnToken);
      outSpace  ();
      outString (templateName);
      outToken  (SemicolonToken);
      unindent  ();
      outLine   ();
      outToken  (BraceCloseToken);
      outLine   ();
      outLine   ();
   }

   protected void outScriptMethod (BasicScriptDeclaration scriptDeclaration) {

      outScriptHeaderComment ((BasicScriptDeclaration) scriptDeclaration, " -- method");
      scriptDeclaration.outModifiers (outputStream);

      outToken  (VoidToken);
      outSpace  ();
      outString (scriptDeclaration.getName());
      outSpace  ();

      outScriptCallParameterListDeclaration (scriptDeclaration);

      if (   scriptDeclaration.typeDeclaration.isInterface ()
          || scriptDeclaration.isAbstract()) {
         outToken (SemicolonToken);
         outLine   ();
         return;
      }

      outSpace  ();
      outToken  (BraceOpenToken);
      indent    ();
      outLine   ();

      if (scriptDeclaration.isMainScript ()) {
         outString ("scriptic.vm.FromJava.mainscript");
         outSpace  ();
         outToken  (ParenthesisOpenToken);
         outToken  (ParenthesisCloseToken);
      } else {
         outToken  (ParenthesisOpenToken);
         outToken  (ParenthesisOpenToken);
         outString ("scriptic.vm.CallerNodeInterface");
         outToken  (ParenthesisCloseToken);
         outSpace  ();
         outString ("script_call");
         outToken  (ParenthesisCloseToken);
      }

      indent    (4);
      outLine   ();
      outToken  (PeriodToken);
      outString ("startScript");
      outSpace  ();
      outToken  (ParenthesisOpenToken);
      setIndent ();
      if (scriptDeclaration.isStatic()) {
          outToken  (NullToken);
      }
      else {
          outToken  (ThisToken);
      }
      outToken  (CommaToken);
      outSpace  ();
      outString (scriptDeclaration.templateGetMethod.name);
      outToken  (ParenthesisOpenToken);
      outToken  (ParenthesisCloseToken);

      outToken  (CommaToken);
      outSpace  ();

      if (scriptDeclaration.hasFormalParameters ())
         outLine ();

      ArrayList<MethodParameterDeclaration> parameters    = scriptDeclaration.getParameters();
      ArrayList<BasicVariableDeclaration> formalIndexesAndParameters = new ArrayList<BasicVariableDeclaration>();
      for (int i=0; i<parameters.size(); i++) {
        formalIndexesAndParameters.add(parameters.get(i));
      }
      if (formalIndexesAndParameters.isEmpty ()) {
         outToken (NullToken);
      }
      else
      {
         outToken  (NewToken);
         outSpace  ();
         outString ("Object");
         outToken  (BracketOpenToken);
         outToken  (BracketCloseToken);
         outToken  (BraceOpenToken);
         for (int i=0; i<formalIndexesAndParameters.size(); i++) {
           if (i>0) outToken (CommaToken);
           outAsObject (formalIndexesAndParameters.get(i));
         }
         outToken  (BraceCloseToken);
      }
      unindent  ();

      outToken  (ParenthesisCloseToken);
      outToken  (SemicolonToken);
      unindent  ();
      unindent  ();
      outLine   ();
      outToken  (BraceCloseToken);
      outLine   ();
      outLine   ();
   }

   void outAsObject (BasicVariableDeclaration v){
     if (v.dataType1.isReference()) {
       outString (v.name);
     }
     else
     {
       outToken  (NewToken);
       outSpace  ();
       ClassType w = v.dataType1.wrapperClass(env);
       outString (w.getName());
       outToken  (ParenthesisOpenToken);
       outString (v.name);
       outToken  (ParenthesisCloseToken);
     }
   }
    
   /* ------------------------------------------------------------------ */

   protected void outScriptCallParameterListDeclaration 
                                     (BasicScriptDeclaration scriptDeclaration) {

      outToken  (ParenthesisOpenToken);

      boolean   firstTime = true;
      if (   scriptDeclaration.isScriptDeclaration ()
          && ((ScriptDeclaration)scriptDeclaration).isMainScript ()) {

         /* Don't declare the Node parameter */
      } else {
         outString ("scriptic.vm.Node script_call");
         firstTime = false;
      }

      scriptDeclaration.outParameterDeclarations (outputStream, firstTime);

      outToken  (ParenthesisCloseToken);
   }

   protected void outMatchExpression (MethodParameterDeclaration parameter) {
    //if (parameter.noOfArrayDimensions == 0)
         outNonArrayMatchExpression (parameter);
   }

   protected void outNonArrayMatchExpression (MethodParameterDeclaration parameter) {
      if (parameter.dataTypeDeclaration.isPrimitiveType()) {

         // return a == old_a;
         outToken  (ReturnToken);
         outSpace  ();
         outString (parameter.getName());
         outSpace  ();
         outToken  (EqualsToken);
         outSpace  ();
         outString ("old_" + parameter.getName());
         outToken  (SemicolonToken);
      } else {

         // if (a == null)
         //    return (old_a == null);
         // else
         //    return (a.equals (old_a));
         outToken  (IfToken);
         outSpace  ();
         outToken  (ParenthesisOpenToken);
         outString (parameter.getName());
         outSpace  ();
         outToken  (EqualsToken);
         outSpace  ();
         outToken  (NullToken);
         outToken  (ParenthesisCloseToken);
         indent    ();
         outLine   ();
         
         outToken  (ReturnToken);
         outSpace  ();
         outToken  (ParenthesisOpenToken);
         outString ("old_" + parameter.getName());
         outSpace  ();
         outToken  (EqualsToken);
         outSpace  ();
         outToken  (NullToken);
         outToken  (ParenthesisCloseToken);
         outToken  (SemicolonToken);
         unindent  ();
         outLine   ();

         outToken  (ElseToken);
         indent    ();
         outLine   ();

         outToken  (ReturnToken);
         outSpace  ();
         outToken  (ParenthesisOpenToken);
         outString (parameter.getName());
         outToken  (PeriodToken);
         outString ("hasEqualValue");
         outSpace  ();
         outToken  (ParenthesisOpenToken);
         outString ("old_" + parameter.getName());
         outToken  (ParenthesisCloseToken);
         outToken  (ParenthesisCloseToken);
         outToken  (SemicolonToken);
         unindent  ();
      }
   }
}



   /*-----------------------------------------------------------------*/
   /*                                                                 */
   /*                       NativeCodeGenerator                       */
   /*                                                                 */
   /*-----------------------------------------------------------------*/

class NativeCodeGenerator extends JavaCodeGenerator {

   Object loadReferenceOnly = new Object(); // flag as arg1 to load reference only, see ScripticCompilerPass8

   /*-------------------------- Constructors -------------------------*/

/***************
   public NativeCodeGenerator (Scanner                  scanner,
                               PreprocessorOutputStream outputStream) {
      super (scanner, outputStream);
   }
**************/
   public NativeCodeGenerator (Scanner                  scanner,
                               PreprocessorOutputStream outputStream,
                               CompilerEnvironment      env) {
      super (scanner, outputStream, env);
   }

   protected void processAssignmentExpression     (FieldDeclaration fieldDeclaration, AssignmentExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
         if (expression.leftExpression instanceof NameExpression
         &&  ((NameExpression)expression.leftExpression).target == NodeDuration.theOne)
         {
            outString ("_n_");
            outToken  (PeriodToken);
            outString ("setDuration");
            outToken  (ParenthesisOpenToken);
            processJavaExpression (fieldDeclaration, expression.rightExpression, level + 1, retValue, arg1, arg2);
            outToken  (ParenthesisCloseToken);
         }
         else
         {
             //does not work as OperatorCodeGenerator any more
             //super.processAssignmentExpression (fieldDeclaration, expression, level, retValue, loadReferenceOnly, arg2);
             //from JavaCodeGenerator, but with 50% loadReferenceOnly:
             processJavaExpression (fieldDeclaration, expression.leftExpression, level + 1, retValue, loadReferenceOnly, arg2);
             outSpace ();
             outToken (expression.operatorToken);
             outSpace ();
             processJavaExpression (fieldDeclaration, expression.rightExpression, level + 1, retValue, arg1, arg2);
         }
   }


   /*-----------------------------------------------------------------*/

   public void outJavaExpressionWithTest (FieldDeclaration fieldDeclaration, JavaExpression expression) {

      // Generate "_n_.success = <expr>;"

      outString ("_n_");
      outToken  (PeriodToken);
      outString ("success");
      outSpace  ();
      outToken  (AssignToken);
      outSpace  ();
      outToken  (ParenthesisOpenToken);
      setIndent ();

      outJavaExpression (fieldDeclaration, expression);

      unindent  ();
      outToken  (ParenthesisCloseToken);
      outToken  (SemicolonToken);
   }

   protected void processExpressionStatement         (RefinementDeclaration refinement, ExpressionStatement statement, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      if (statement.specialCode == SpecialCode.none) {
         super.processExpressionStatement (refinement, statement, level, retValue, arg1, arg2);
         return;
      }

      // statement.specialCode == SpecialCode.singleSuccessTest or SpecialCode.doubleSuccessTest
      // Success testing expression "xxxx ??"
      // Code to be generated "if (!_n_.setSuccessTrueOrNull(xxxx)) return;"

      outToken  (IfToken);
      outSpace  ();
      outToken  (ParenthesisOpenToken);
      outToken  (ExclamationToken);
      outSpace  ();
      outString  ("_n_");
      outToken  (PeriodToken);
      String methodName = statement.specialCode == SpecialCode.singleSuccessTest
      					? "setSuccess": "setSuccessTrueOrNull";
      outString ("setSuccessTrueOrNull");
      outToken  (ParenthesisOpenToken);

      processJavaExpression (refinement, statement.expression, level + 1, retValue, arg1, arg2);

      outToken  (ParenthesisCloseToken);
      outToken  (ParenthesisCloseToken);
      outSpace  ();
      outToken  (ReturnToken);
      outToken  (SemicolonToken);
   }

   protected void processSpecialNameExpression    (FieldDeclaration fieldDeclaration, SpecialNameExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      if (expression.getToken() == ScriptToken) {
         // This is the representation of "script.this".
         // Generate a reference to the current script call node.
         outString ("_n_");
      } else if (expression.isThis())
      {
        outString (fieldDeclaration.typeDeclaration.getName());
        outToken  (PeriodToken);
        outToken  (ThisToken);
      } else if (expression.isSuper())
      {
        outString (fieldDeclaration.typeDeclaration.getName());
        outToken  (PeriodToken);
        outToken  (SuperToken);
      }
      else
      {
         super.processSpecialNameExpression (fieldDeclaration, expression, level, retValue, arg1, arg2);
      }
   }


   protected void processNameExpression(FieldDeclaration fieldDeclaration, NameExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
     try {
      Variable target = expression.target;
      BasicScriptDeclaration scriptDeclaration = (BasicScriptDeclaration) fieldDeclaration;

      if (expression.specialCode == SpecialCode.compoundName) {
         // Compound name which has been turned into a sequence
         // of FieldAccessExpressions
         processJavaExpression (fieldDeclaration, (JavaExpression)expression.specialObject, level + 1, retValue, arg1, arg2);
         return;
      }

/***********************************/

      if (expression.specialCode == SpecialCode.matchingTest
      ||  expression.specialCode == SpecialCode.isOutParameterOrAsOutParameter
      ||  expression.specialCode == SpecialCode.isMatchingParameterOrAsMatchingParameter) {

         // i!!  specialCode = 1    (!_n_.isForced(2) || p == old_p)
         // i!   specialCode = 2      _n_.isForced(2)
         // i?!  specialCode = 3
         // i?   specialCode = 4      _n_.isOut(2)

         if (expression.specialCode == SpecialCode.matchingTest) {
            outToken (ParenthesisOpenToken);
            outToken (ExclamationToken);
         }
         outString  ("_n_");
         outToken  (PeriodToken);
         if (expression.specialCode == SpecialCode.isOutParameterOrAsOutParameter) {
            outString ("isOut");
         } else {
            outString ("isForced");
         }
         outToken  (ParenthesisOpenToken);
         int index = scriptDeclaration.paramIndexOf (
                                    (NormalOrOldScriptParameter)target);
         outInteger (index);
         outToken  (ParenthesisCloseToken);
         if (expression.specialCode == SpecialCode.matchingTest) {
            outSpace ();
            outToken (BooleanOrToken);
            outSpace ();
            scriptDeclaration.outLocalDataAccess (outputStream, target, env, true);
            outSpace ();
            outToken (EqualsToken);
            outSpace ();
            scriptDeclaration.outLocalDataAccess (outputStream, ((ScriptParameter)target).oldVersion, env, true);
/*            
            // A little "Cheat"... look at the code in BasicScriptDeclaration
            BasicScriptDeclaration owner 
                  = (BasicScriptDeclaration) target.owner.source;
            int oldParamDataIndex = owner.getPartnerIndex ();

            // Special treatment for BIDIRECTIONAL CHANNELS:
            // Must get "old" parameter from paramData(1)
            // instead of paramData(0)
            if (   owner.isChannelDeclaration ()
                && ((ChannelDeclaration)owner).isBidirectionalChannel) {
               oldParamDataIndex = 1;
            }

            scriptDeclaration.outAccess (outputStream,
                                         owner.getParamClassName (),
                                         "paramData",
                                         false,
                                         oldParamDataIndex,
                                         "old_" + targetDeclaration.getName());
*/
            outToken (ParenthesisCloseToken);
         }
      } else {
        if (target != null
        &&  target ==    NodeDuration.theOne) {
            outString ("_n_");
            outToken  (PeriodToken);
            outString ("getDuration");
            outToken  (ParenthesisOpenToken);
            outToken  (ParenthesisCloseToken);
        } else if (target != null
        && (target == env.scripticVmNodePass
          ||target == env.scripticVmNodePriority
          ||target == env.scripticVmNodeSuccess)) {

            outString ("_n_");
            outToken  (PeriodToken);
            outString (expression.name);
        }
        else if (target != null
         && (target.isScriptParameter()
           ||target.isOldParameter()
           ||target.isScriptLocalVariable())) {
            scriptDeclaration.outLocalDataAccess (outputStream, target, env, arg1==loadReferenceOnly);
         } else {
            outString (expression.getName());
         }
      }
/*******************/
     } catch (ByteCodingException e) {env.handleByteCodingException (e, typeDeclaration, expression);
     } catch (IOException e) {parserError (2, e.getMessage());}
   }

   protected void processFieldAccessExpression    (FieldDeclaration fieldDeclaration, FieldAccessExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
    try {	   
      if (!expression.primaryExpression.isOldIdentifier()) { // quick hack...
          processJavaExpression (fieldDeclaration, expression.primaryExpression, level + 1, retValue, arg1, arg2);
          outToken  (PeriodToken);
          outString (expression.getName());
      }
      else if (expression.target != null) {
         ((BasicScriptDeclaration) fieldDeclaration).outLocalDataAccess (
                                              outputStream, expression.target, env, false);
      } else {
         outString (expression.getName());
      }
    } catch (ByteCodingException e) {env.handleByteCodingException (e, typeDeclaration, expression);
    } catch (IOException e) {parserError (2, e.getMessage());}
   }

}


   /*-----------------------------------------------------------------*/
   /*                                                                 */
   /*                        CodeProcGenerator                        */
   /*                                                                 */
   /*-----------------------------------------------------------------*/


class CodeProcGenerator extends CodeGeneratorParseTreeEnumerator {

   public int noOfCodeFragments = 0, currentCodeIndex = 0;
   NativeCodeGenerator nativeCodeGenerator;


   /*-------------------------- Constructors -------------------------*/

/**********
   public CodeProcGenerator (Scanner                  scanner,
                             PreprocessorOutputStream outputStream) {
      super (scanner, outputStream);
      nativeCodeGenerator = new NativeCodeGenerator (scanner, outputStream);
   }
*********/
   public CodeProcGenerator (Scanner                  scanner,
                             PreprocessorOutputStream outputStream,
                             CompilerEnvironment      env) {
      super (scanner, outputStream, env);
      nativeCodeGenerator = new NativeCodeGenerator (scanner, outputStream, env);
   }

   /*-----------------------------------------------------------------*/

   public Object processScriptExpression (BasicScriptDeclaration scriptDeclaration, ScriptExpression expression) {

      CodeCounter codeCounter = new CodeCounter ();
      noOfCodeFragments       = codeCounter.roughNoOfCodeFragments 
                                          (scriptDeclaration.scriptExpression);

      outToken  (PublicToken);
      outSpace  ();
      scriptDeclaration.outModifiers (outputStream, 
                                      SynchronizedFlag);
      outToken  (VoidToken);
      outSpace  ();
      outString ("code");
      outSpace  ();
      outToken  (ParenthesisOpenToken);
      outString ("scriptic.vm.Node");
      outSpace  ();
      outString ("_n_");
      outToken  (CommaToken);
      outSpace  ();
      outToken  (IntToken);
      outSpace  ();
      outString ("_i_");
      outToken  (ParenthesisCloseToken);

      outSpace  ();
      outToken  (BraceOpenToken);


      do { // bogus loop
         if (noOfCodeFragments == 0)
            break;

         if (noOfCodeFragments == 1) {
            processScriptExpression (scriptDeclaration, expression, 0, null, null, null);
            break;
         }

         indent    ();
         outLine   ();

         outToken  (SwitchToken);
         outSpace  ();
         outToken  (ParenthesisOpenToken);
         outString ("_i_");
         outToken  (ParenthesisCloseToken);
         outSpace  ();
         outToken  (BraceOpenToken);
         indent    ();

         processScriptExpression (scriptDeclaration, expression, 0, null, null, null);

         unindent  ();
         outLine   ();
         outToken  (BraceCloseToken);
         unindent  ();

         break;
      } while (false);

      outLine   ();
      outToken  (BraceCloseToken);
      outLine   ();
      outLine   ();

      return null;
   }

   /*-----------------------------------------------------------------*/

   protected void outCodeFragmentCaseTagPrefix () {
      if (noOfCodeFragments > 1) {
         outLine   ();
         outToken  (CaseToken);
         outSpace  ();
         outString (String.valueOf (currentCodeIndex++));
         outSpace  ();
         outToken  (ColonToken);
         indent    (4);
         outLine   ();

      } else {
         indent    ();
         outLine   ();
      }
   }

   protected void outCodeFragmentCaseTagPostfix () {
      outCodeFragmentCaseTagPostfix (false);
   }

   protected void outCodeFragmentCaseTagPostfix (boolean wasEmpty) {
      if (noOfCodeFragments > 1) {
         if (!wasEmpty)
            outLine ();
         outToken  (BreakToken);
         outToken  (SemicolonToken);
      }
      unindent  ();
   }

   /*-----------------------------------------------------------------*/

   protected void processNativeCodeFragment          (BasicScriptDeclaration scriptDeclaration, NativeCodeFragment expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      if (   expression.durationAssignment != null
          || expression.priorityAssignment != null) {
         outCodeFragmentCaseTagPrefix ();
         outToken  (BraceOpenToken);
         indent    ();
         outLine   ();
   
         if (expression.durationAssignment != null)
            nativeCodeGenerator.processJavaStatement (scriptDeclaration, expression.durationAssignment, level + 1, retValue, arg1, arg2);
         if (expression.priorityAssignment != null)
            nativeCodeGenerator.processJavaStatement (scriptDeclaration, expression.priorityAssignment, level + 1, retValue, arg1, arg2);

         unindent  ();
         outLine   ();
         outToken  (BraceCloseToken);
         outCodeFragmentCaseTagPostfix ();
      }
      if (!expression.isEmpty())
      {
        ArrayList<JavaStatement> savedStatementStack = nativeCodeGenerator.statementStack;
        nativeCodeGenerator.statementStack = new ArrayList<JavaStatement>();
        outCodeFragmentCaseTagPrefix ();
        outToken  (BraceOpenToken);
        indent    ();
        if (!expression.statements.isEmpty ())
           outLine   ();

        nativeCodeGenerator.processJavaStatements (scriptDeclaration, expression.statements, level + 1, retValue, arg1, arg2);

        unindent  ();
        if (!expression.statements.isEmpty ())
          outLine   ();
        else
           outSpace  ();
        outToken  (BraceCloseToken);
        outCodeFragmentCaseTagPostfix ();
        nativeCodeGenerator.statementStack = savedStatementStack;
      }
   }

   protected void processEventHandlingCodeFragment   (BasicScriptDeclaration scriptDeclaration, EventHandlingCodeFragment expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      if (   expression.anchorExpression   != null) {

        outCodeFragmentCaseTagPrefix ();

        outString ("_n_");
        outToken  (PeriodToken);
        outString ("setAnchor");
        outSpace  ();
        outToken  (ParenthesisOpenToken);
        setIndent ();

        nativeCodeGenerator.outJavaExpression ((FieldDeclaration) scriptDeclaration,
                                               expression.anchorExpression);

        unindent  ();
        outToken  (ParenthesisCloseToken);
        outToken  (SemicolonToken);
 
        outCodeFragmentCaseTagPostfix ();
      }
      processNativeCodeFragment (scriptDeclaration, (NativeCodeFragment) expression, level, retValue, arg1, arg2);
   }

   protected void processIfScriptExpression      (BasicScriptDeclaration scriptDeclaration, IfScriptExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      if (expression.condition.isConstant()) return;
      outCodeFragmentCaseTagPrefix ();
      nativeCodeGenerator.outJavaExpressionWithTest ((FieldDeclaration) scriptDeclaration,
                                                     expression.condition);
      outCodeFragmentCaseTagPostfix ();

      processScriptExpression (scriptDeclaration, expression.ifTerm, level + 1, retValue, arg1, arg2);
      if (expression.elseTerm != null)
         processScriptExpression (scriptDeclaration, expression.elseTerm, level + 1, retValue, arg1, arg2);
   }

   protected void processWhileScriptExpression   (BasicScriptDeclaration scriptDeclaration, WhileScriptExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      if (expression.condition.isConstant()) return;
      outCodeFragmentCaseTagPrefix ();
      nativeCodeGenerator.outJavaExpressionWithTest ((FieldDeclaration) scriptDeclaration,
                                                     expression.condition);
      outCodeFragmentCaseTagPostfix ();
   }

   protected void processForScriptExpression    (BasicScriptDeclaration scriptDeclaration, ForScriptExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      if (expression.initExpression != null)
      {
         outCodeFragmentCaseTagPrefix ();
         nativeCodeGenerator.outJavaExpression ((FieldDeclaration) scriptDeclaration,
                                                expression.initExpression);
         outToken (SemicolonToken);
         outCodeFragmentCaseTagPostfix ();
      }
      if (expression.condition != null
      && !expression.condition.isConstant())
      {
        outCodeFragmentCaseTagPrefix ();
        nativeCodeGenerator.outJavaExpressionWithTest ((FieldDeclaration) scriptDeclaration,
                                                        expression.condition);
        outCodeFragmentCaseTagPostfix ();
      }
      if (expression.loopExpression != null)
      {
        outCodeFragmentCaseTagPrefix ();
        nativeCodeGenerator.outJavaExpression ((FieldDeclaration) scriptDeclaration,
                                                expression.loopExpression);
        outToken (SemicolonToken);
        outCodeFragmentCaseTagPostfix ();
      }
   }

   protected void processSwitchScriptExpression  (BasicScriptDeclaration scriptDeclaration, SwitchScriptExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      outCodeFragmentCaseTagPrefix ();

      outToken  (SwitchToken);
      outSpace  ();
      outToken  (ParenthesisOpenToken);
      nativeCodeGenerator.outJavaExpression ((FieldDeclaration) scriptDeclaration,
                                             expression.switchExpression);
      outToken  (ParenthesisCloseToken);
      outSpace  ();
      outToken  (BraceOpenToken);
      indent    ();

      int index = 1;
      for (CaseTagScriptExpression caseTag: expression.caseTags) {
         //NOTE: We use the generic "level" parameter to pass the case group index
         processCaseTagScriptExpression (scriptDeclaration, caseTag, index, retValue, arg1, arg2);
      }

      unindent  ();
      outLine   ();
      outToken  (BraceCloseToken);

      outCodeFragmentCaseTagPostfix ();

      for (CaseTagScriptExpression caseTag: expression.caseTags) {
         processScriptExpression (scriptDeclaration, caseTag.caseExpression, level + 1, retValue, arg1, arg2);
      }
   }

   protected void processCaseTagScriptExpression (BasicScriptDeclaration scriptDeclaration, CaseTagScriptExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {

      for (JavaExpression tag: expression.tags) {
         outLine   ();

         if (   tag instanceof SpecialNameExpression
             && ((SpecialNameExpression) tag).getToken() == DefaultToken) {
            outToken (DefaultToken);
         } else {
            outToken  (CaseToken);
            outSpace  ();
            nativeCodeGenerator.outJavaExpression ((FieldDeclaration) scriptDeclaration,
            		tag);
         }
         outSpace  ();
         outToken  (ColonToken);
      }
      indent    (4);
      outLine   ();

      outString ("_n_");
      outToken  (PeriodToken);
      outString ("pass");
      outSpace  ();
      outToken  (AssignToken);
      outSpace  ();
      outString (String.valueOf (level));
      outToken  (SemicolonToken);
      outLine   ();

      outToken  (BreakToken);
      outToken  (SemicolonToken);
      unindent  ();
   }

   protected void processScriptCallExpression    (BasicScriptDeclaration scriptDeclaration, ScriptCallExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {

      outCodeFragmentCaseTagPrefix ();

      nativeCodeGenerator.outJavaExpression ((FieldDeclaration) scriptDeclaration,
                                                expression.scriptAccessExpression);
      outSpace  ();
      outToken  (ParenthesisOpenToken);
      outString ("_n_");

      if (expression.parameterList == null
      ||  expression.parameterList.parameterExpressions.isEmpty ()) {
         outToken (ParenthesisCloseToken);
         outToken (SemicolonToken);
         outCodeFragmentCaseTagPostfix ();
         return;
      }

      ArrayList<ScriptCallParameter> outputAndAdaptingParameters = new ArrayList<ScriptCallParameter>();
      if (expression.parameterList != null)
         processScriptCallParameterList (scriptDeclaration, expression, expression.parameterList, level + 1, retValue, outputAndAdaptingParameters, arg2);

      outToken (ParenthesisCloseToken);
      outToken (SemicolonToken);
      //if (containsCodeFragment)
      //{
      //      unindent  ();
      //      outLine   ();
      //      outToken  (BraceCloseToken);
      //}
      outCodeFragmentCaseTagPostfix ();

      for (ScriptCallParameter parameter: outputAndAdaptingParameters) {

         // Parameter transfer
         // str = (String)_n_.calleeParams().objectAt(0);
         // ((C__a_local_003)_n_.localData[3]).j = _n.calleeParams().intAt(2);

         outCodeFragmentCaseTagPrefix ();

         nativeCodeGenerator.processJavaExpression (scriptDeclaration,
                                                    parameter.expression,
                                                    0, 
                                                    null, 
                                                    nativeCodeGenerator.loadReferenceOnly, 
                                                    null);
         outSpace  ();
         outToken  (AssignToken);
         outSpace  ();

         if (!parameter.dataType.isPrimitive()) {
            outToken  (ParenthesisOpenToken);
            outString (parameter.dataType.getNameWithDots());
            outToken  (ParenthesisCloseToken);
         }
         outToken  (ParenthesisOpenToken);
         outToken  (ParenthesisOpenToken);
         outString (parameter.dataType.holderClass(env).getNameWithDots());
         outToken  (ParenthesisCloseToken);
         outString ("_n_");
         outToken  (PeriodToken);
         outString ("calleeParams");
         outToken  (ParenthesisOpenToken);
         outToken  (ParenthesisCloseToken);
         outToken  (BracketOpenToken);
         outString (String.valueOf (parameter.actualIndex));
         outToken  (BracketCloseToken);
         outToken  (ParenthesisCloseToken);
         outToken  (PeriodToken);
         outString ("value");
         outToken  (SemicolonToken);
         outCodeFragmentCaseTagPostfix ();
/************/
      }
   }

   protected void processScriptCallParameterList  (BasicScriptDeclaration scriptDeclaration, ScriptCallExpression scriptCall, MethodCallParameterList parameterList, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      if (parameterList.parameterExpressions.isEmpty()) return;
      for (JavaExpression obj: parameterList.parameterExpressions) {
    	  ScriptCallParameter scp = (ScriptCallParameter) obj;
         processScriptCallParameter (scriptDeclaration, scriptCall, scp, level + 1, retValue, arg1, arg2);
      }
   }

   protected void processScriptCallFormalIndex    (BasicScriptDeclaration scriptDeclaration, ScriptCallExpression scriptCall, JavaExpression formalIndex, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      outToken  (CommaToken);
      outSpace  ();
      nativeCodeGenerator.outJavaExpression (scriptDeclaration,
                                             formalIndex);
   }

   protected void processScriptCallParameter      (BasicScriptDeclaration scriptDeclaration, ScriptCallExpression scriptCall, ScriptCallParameter parameter, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {

	  ArrayList<ScriptCallParameter> outputAndAdaptingParameters = (ArrayList<ScriptCallParameter>) arg1;

      outToken  (CommaToken);
      outSpace  ();
      nativeCodeGenerator.outJavaExpression (scriptDeclaration,
                                             parameter.expression);

      if (   !parameter.isOutput 
          && !parameter.isAdapting)
         return;

      outputAndAdaptingParameters.add(parameter);

/**********
      // To keep it consistent, only process the params if they were
      // resolved with no errors
      if (parameter.isAdapting) {
         if (parameter.formalParameter != null) 
            outputAndAdaptingParameters.addElement (parameter);
      } else {
         // parameter.isOutput
         if (parameter.targetDeclaration != null) 
            outputAndAdaptingParameters.addElement (parameter);
      }
*************/
   }

   protected void processScriptLocalVariable  (BasicScriptDeclaration scriptDeclaration, LocalScriptVariableDeclaration variableDeclaration, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      outCodeFragmentCaseTagPrefix ();

      ScriptLocalVariable sl = (ScriptLocalVariable) variableDeclaration.target;
      ClassType holderClass = variableDeclaration.dataType().holderClass(env);

      outString ("_n_");
      outToken  (PeriodToken);
      outString ("setLocalData");
      outSpace  ();
      outToken  (ParenthesisOpenToken);
      outInteger(sl.slot);
      outToken  (CommaToken);
      outSpace  ();
      outToken  (NewToken);
      outSpace  ();
      outString (holderClass.getNameWithDots());
      outSpace  ();
      outToken  (ParenthesisOpenToken);

      if (variableDeclaration.initializer != null) {
         nativeCodeGenerator.outJavaExpression ((FieldDeclaration) scriptDeclaration,
                                                variableDeclaration.initializer);
      }

      outToken  (ParenthesisCloseToken);
      outToken  (ParenthesisCloseToken);
      outToken  (SemicolonToken);
      outCodeFragmentCaseTagPostfix ();
   }

   protected void processPrivateScriptVariable  (BasicScriptDeclaration scriptDeclaration, PrivateScriptVariableDeclaration variableDeclaration, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      outCodeFragmentCaseTagPrefix ();
      //String localDataClassName
      //         ="";//= scriptDeclaration.getLocalDataClassName (variableDeclaration);
      ClassType holderClass = variableDeclaration.dataType().holderClass(env);
      ScriptLocalVariable sl = (ScriptLocalVariable) variableDeclaration.target;

      outString ("_n_.setLocalData");
      outSpace  ();
      outToken  (ParenthesisOpenToken);
      outInteger(sl.slot);
      outToken  (CommaToken);
      outSpace  ();
      outToken  (NewToken);
      outSpace  ();
      outString (holderClass.getNameWithDots());
      outSpace  ();
      outToken  (ParenthesisOpenToken);


      BasicVariableDeclaration targetDeclaration 
               = variableDeclaration.targetDeclaration;

         /* targetDeclaration will be null if the private declaration
            could not be resolved (currently, to a local declaration) */
      if (targetDeclaration == null) {
         outString (variableDeclaration.getName ());
      } else {
    	try {
         scriptDeclaration.outLocalDataAccess (outputStream, (Variable) targetDeclaration.targetField(), env, false);
        } catch (ByteCodingException e) {parserError (2, e.getMessage());
        } catch (IOException e) {parserError (2, e.getMessage());}
      }

      outToken  (ParenthesisCloseToken);
      outToken  (ParenthesisCloseToken);
      outToken  (SemicolonToken);
      outCodeFragmentCaseTagPostfix ();
   }
}


   /*-----------------------------------------------------------------*/

/* CodeCounter -- a helper that checks whether there are zero, one, or multiple code fragments */

class CodeCounter extends ScripticParseTreeEnumerator {

   public int roughNoOfCodeFragments (ScriptExpression expression) {
      ReturnValueHolder retValue = new ReturnValueHolder ();
      retValue.value = new Integer (0);
      processScriptExpression (null, expression, 0, retValue, null, null);
      return ((Integer)retValue.value).intValue ();
   }

   /*------------------------ Counting routines ----------------------*/

   protected boolean isAlreadyGreaterThanOne (ReturnValueHolder retValue) {
      return ((Integer)retValue.value).intValue () > 1;
   }

   protected boolean incrementAndTest (ReturnValueHolder retValue) {
      return incrementAndTest (retValue, 1);
   }

   protected boolean incrementAndTest (ReturnValueHolder retValue, int count) {
      /* Increment the count in the retValue. Answer true if it's > 1.
         Note how unbelievably cumbersome it is to increment a Java "Integer". */
      int newCount   = ((Integer)retValue.value).intValue () + count;
      retValue.value = new Integer (newCount);
      return newCount > 1;
   }

   /*-----------------------------------------------------------------*/

/* The following items generate a code fragment:

   local data
   private data
   script call
      every output or adapting parameter in a script call
   code fragment ({ {? {* {: {. {..)
   if
   while
   for (three pieces)
   switch
   (activation   - contains code fragment)
   (deactivation - contains code fragment)
*/

   protected void processScriptExpression (BasicScriptDeclaration scriptDeclaration, ScriptExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      /* Early exit if we already have multiple code fragments */
      if (isAlreadyGreaterThanOne (retValue)) return;
      super.processScriptExpression (scriptDeclaration, expression, level, retValue, arg1, arg2);
   }
   protected void processScriptExpressions (BasicScriptDeclaration scriptDeclaration, ArrayList<ScriptExpression> expressions, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      /* Early exit if we already have multiple code fragments */
      for (ScriptExpression se: expressions) {
         processScriptExpression (scriptDeclaration, se, level, retValue, arg1, arg2);
         if (isAlreadyGreaterThanOne (retValue)) return;
      }
   }

   protected void processNativeCodeFragment          (BasicScriptDeclaration scriptDeclaration, NativeCodeFragment expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      if (   expression.durationAssignment != null
          || expression.priorityAssignment != null)
         incrementAndTest (retValue);
      incrementAndTest (retValue);
   }
   protected void processEventHandlingCodeFragment   (BasicScriptDeclaration scriptDeclaration, EventHandlingCodeFragment expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      incrementAndTest (retValue); // for anchorExpression
      processNativeCodeFragment (scriptDeclaration, (NativeCodeFragment) expression, level, retValue, arg1, arg2);
   }
   protected void processIfScriptExpression      (BasicScriptDeclaration scriptDeclaration, IfScriptExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      if (incrementAndTest (retValue)) return;
      super.processIfScriptExpression (scriptDeclaration, expression, level, retValue, arg1, arg2);
   }
   protected void processWhileScriptExpression   (BasicScriptDeclaration scriptDeclaration, WhileScriptExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      incrementAndTest (retValue);
   }
   protected void processForScriptExpression    (BasicScriptDeclaration scriptDeclaration, ForScriptExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      incrementAndTest (retValue, 3);
   }
   protected void processSwitchScriptExpression  (BasicScriptDeclaration scriptDeclaration, SwitchScriptExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      if (incrementAndTest (retValue)) return;
      super.processSwitchScriptExpression (scriptDeclaration, expression, level, retValue, arg1, arg2);
      /* Case tag expressions do not generate separate code fragments */
   }
   protected void processScriptCallExpression    (BasicScriptDeclaration scriptDeclaration, ScriptCallExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      if (incrementAndTest (retValue)) return;
      super.processScriptCallExpression (scriptDeclaration, expression, level, retValue, arg1, arg2);
   }
   protected void processScriptCallParameter      (FieldDeclaration fieldDeclaration, ScriptCallExpression scriptCall, ScriptCallParameter parameter, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      if (   !parameter.isOutput 
          && !parameter.isAdapting)
         return;
/*********************
      // To keep it consistent, only count the params if they were
      // resolved with no errors
      if (parameter.isAdapting) {
         if (parameter.formalParameter != null) 
            incrementAndTest (retValue);
      } else {
         // parameter.isOutput
         if (parameter.targetDeclaration != null) 
            incrementAndTest (retValue);
      }
******************/
   }
   protected void processScriptLocalDataDeclaration  (BasicScriptDeclaration scriptDeclaration, ScriptLocalDataDeclaration expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      if (incrementAndTest (retValue, expression.variables.variables.size ()))
         return;
      processScriptExpression (scriptDeclaration, expression.scriptTerm, level + 1, retValue, arg1, arg2);
   }
   protected void processPrivateScriptDataDeclaration (BasicScriptDeclaration scriptDeclaration, PrivateScriptDataDeclaration expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      if (incrementAndTest (retValue, expression.variables.size ()))
         return;
      processScriptExpression (scriptDeclaration, expression.scriptTerm, level + 1, retValue, arg1, arg2);
   }
}

   /*-----------------------------------------------------------------*/
   /*                                                                 */
   /*                     ScriptStringGenerator                       */
   /*                                                                 */
   /*-----------------------------------------------------------------*/


class ScriptStringGenerator extends ScripticParseTreeEnumerator {
   Scanner scanner;
   public StringBuffer scriptStringBuffer = new StringBuffer ();


   public ScriptStringGenerator (Scanner scanner) {
      this.scanner = scanner;
   }

   public String       getScriptString () {
      return scriptStringBuffer.toString ();
   }

   public Object processScriptExpression (BasicScriptDeclaration scriptDeclaration, ScriptExpression expression) {
      // scriptDeclaration must not be null!
      scriptStringBuffer = new StringBuffer (1024);
      TypeDeclaration typeDeclaration = scriptDeclaration.typeDeclaration;
      CompilationUnit compilationUnit = typeDeclaration.compilationUnit;

      if (compilationUnit.packageStatement != null) {
         scriptStringBuffer.append ("package ");
         scriptStringBuffer.append (compilationUnit.packageStatement.getName ());
         scriptStringBuffer.append (' ');
      }
      scriptStringBuffer.append ("class ");
      scriptStringBuffer.append (typeDeclaration.getName ());
      scriptStringBuffer.append (' ');

      scriptStringBuffer.append ("script ");
      scriptStringBuffer.append (scriptDeclaration.getScriptStringName ());
      scriptStringBuffer.append (" = ");

      return super.processScriptExpression (scriptDeclaration, expression);
   }

   protected void processInfixExpression             (BasicScriptDeclaration scriptDeclaration, InfixExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      boolean           firstTime = true;

      /* No need to generate any extra parentheses. Because the expression was
         parsed from source code containing identical operators with identical
         precedences, any explicit precedences will have been already indicated
         with parentheses in the source code. Such parentheses will have been
         parsed as NestedScriptExpressions which will generate the same parentheses
         in the script string. */
      
      for (ScriptExpression se: expression.expressions) {
         if (firstTime)
            firstTime = false;
         else
            scriptStringBuffer.append (expression.getName() + " ");
         processScriptExpression (scriptDeclaration, se, level + 1, retValue, arg1, arg2);
      }

      // NO call to super!
   }

   protected void processUnaryScriptExpression       (BasicScriptDeclaration scriptDeclaration, UnaryScriptExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      scriptStringBuffer.append (expression.getName() + " ");
      processScriptExpression (scriptDeclaration, expression.primaryExpression, level + 1, retValue, arg1, arg2);
      // NO call to super!
   }

   protected void processSpecialNameScriptExpression (BasicScriptDeclaration scriptDeclaration, SpecialNameScriptExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      scriptStringBuffer.append (expression.getName() + " ");
   }

   protected void processNestedScriptExpression      (BasicScriptDeclaration scriptDeclaration, NestedScriptExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      scriptStringBuffer.append (Scanner.tokenRepresentation (expression.startingDelimiter));
      processScriptExpression (scriptDeclaration, expression.subExpression, level + 1, retValue, arg1, arg2);
      scriptStringBuffer.append (Scanner.tokenRepresentation (expression.endingDelimiter));
   }

   protected void processNativeCodeFragment          (BasicScriptDeclaration scriptDeclaration, NativeCodeFragment expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
	  if (expression.anchorExpression!=null)
	  {
          scriptStringBuffer.append ("@");
	  }
      scriptStringBuffer.append (Scanner.tokenRepresentation (expression.startingDelimiter));

      if (   expression.durationAssignment != null
          || expression.priorityAssignment != null) {

         if (expression.durationAssignment != null)
            scriptStringBuffer.append ("<");
         else
            scriptStringBuffer.append (">");
      }

      scriptStringBuffer.append (Scanner.tokenRepresentation (expression.endingDelimiter));
   }

   protected void processEventHandlingCodeFragment   (BasicScriptDeclaration scriptDeclaration, EventHandlingCodeFragment expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      scriptStringBuffer.append (Scanner.tokenRepresentation (expression.startingDelimiter));
      scriptStringBuffer.append(' ');
      scriptStringBuffer.append (Scanner.tokenRepresentation (expression.endingDelimiter));
   }

   protected void processActivationCode          (BasicScriptDeclaration scriptDeclaration, ActivationCode expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      scriptStringBuffer.append ("{}< ");
      processScriptExpression  (scriptDeclaration, expression.scriptTerm, level + 1, retValue, arg1, arg2);
   }

   protected void processDeactivationCode            (BasicScriptDeclaration scriptDeclaration, DeactivationCode expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      processScriptExpression  (scriptDeclaration, expression.scriptTerm, level + 1, retValue, arg1, arg2);
      scriptStringBuffer.append (" >{} ");
   }

   protected void processConditionalScriptExpression (BasicScriptDeclaration scriptDeclaration, ConditionalScriptExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      processScriptExpression (scriptDeclaration, expression.condition, level + 1, retValue, arg1, arg2);
      scriptStringBuffer.append ("? ");
      processScriptExpression (scriptDeclaration, expression.successTerm, level + 1, retValue, arg1, arg2);
      if (expression.failureTerm != null) {
         scriptStringBuffer.append (": ");
         processScriptExpression (scriptDeclaration, expression.failureTerm, level + 1, retValue, arg1, arg2);
      }
   }

   protected void processIfScriptExpression      (BasicScriptDeclaration scriptDeclaration, IfScriptExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      scriptStringBuffer.append ("if ");
      processScriptExpression (scriptDeclaration, expression.ifTerm, level + 1, retValue, arg1, arg2);
      if (expression.elseTerm != null) {
         scriptStringBuffer.append ("else ");
         processScriptExpression (scriptDeclaration, expression.elseTerm, level + 1, retValue, arg1, arg2);
      }
   }

   protected void processWhileScriptExpression   (BasicScriptDeclaration scriptDeclaration, WhileScriptExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      scriptStringBuffer.append ("while ");
   }

   protected void processForScriptExpression    (BasicScriptDeclaration scriptDeclaration, ForScriptExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      scriptStringBuffer.append ("for ");
   }

   protected void processSwitchScriptExpression  (BasicScriptDeclaration scriptDeclaration, SwitchScriptExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      scriptStringBuffer.append ("switch (");

      for (CaseTagScriptExpression caseTag: expression.caseTags) {
         scriptStringBuffer.append ("case ");
         processScriptExpression (scriptDeclaration, caseTag.caseExpression, level + 1, retValue, arg1, arg2);
         // this skips one level number compared to the superclass...
         // but we aren't using the level number here, anyway.
      }

      scriptStringBuffer.append (")");
      // NO call to super!
   }

   protected void processScriptCallExpression    (BasicScriptDeclaration scriptDeclaration, ScriptCallExpression expression, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      scriptStringBuffer.append (expression.getName ());
      if (expression.isScriptChannelSend   ()) scriptStringBuffer.append ("<<");
      if (expression.isScriptChannelReceive()) scriptStringBuffer.append (">>");
      scriptStringBuffer.append ("(");

      boolean              firstTime = true;
      for (JavaExpression obj: expression.parameterList.parameterExpressions) {
         ScriptCallParameter param = (ScriptCallParameter) obj;

         if (firstTime)
            firstTime = false;
         else
            scriptStringBuffer.append (",");

         if      (param.isOutput) 
                           scriptStringBuffer.append ("?");
         else if (param.isForcing) 
                           scriptStringBuffer.append ("!");
         else if (param.isAdapting) {
                           scriptStringBuffer.append (param.formalParameterIndex)
                                             .append ("?!");
         }
      }

      scriptStringBuffer.append (")");
      // NO call to super!
   }

   protected void processScriptLocalVariable  (BasicScriptDeclaration scriptDeclaration, LocalScriptVariableDeclaration variableDeclaration, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      scriptStringBuffer.append ("int ");
      // NO call to super! Ignore the initializer.
   }

   protected void processPrivateScriptVariable  (BasicScriptDeclaration scriptDeclaration, PrivateScriptVariableDeclaration variableDeclaration, int level, ReturnValueHolder retValue, Object arg1, Object arg2) {
      scriptStringBuffer.append ("int ");
   }
}


